package com.woniuxy.project115.kaer.tenant.interceptor;

import com.woniuxy.project115.kaer.commons.util.JwtUtil;
import com.woniuxy.project115.kaer.tenant.util.RedisKey;
import io.jsonwebtoken.Claims;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author HuH
 * @version 1.0
 * @CreatTime 2025-02-19 20:04
 * @description 登录拦截器 token校验
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //过滤掉options请求（预检请求）
        if (request.getMethod().equalsIgnoreCase("options")) {
            return true;
        }
        if (request.getRequestURI().contains("/imserver/sj")) {
            return true;
        }
        String accessToken = request.getHeader("token");
        if (accessToken == null) {
            throw new Exception("令牌不存在");
        }

        //判断accessToken是否过期
        if (stringRedisTemplate.hasKey(RedisKey.ACCESS_EXPIRE(accessToken))) {
            return true;
        }

        synchronized (accessToken.intern()) {
            //防止在并发情况下重复续期
            if (stringRedisTemplate.hasKey(RedisKey.ACCESS_EXPIRE(accessToken))) {
                return true;
            }

            //判断长token是否过期
            if (!(stringRedisTemplate.getExpire(RedisKey.ACCESS_REFRESH(accessToken), TimeUnit.SECONDS)/60 >= 5)) {
                throw new Exception("令牌已过期");
            }

            String refreshToken = stringRedisTemplate.opsForValue().get(RedisKey.ACCESS_REFRESH(accessToken));
            Claims claims = JwtUtil.parseToken(refreshToken);
            HashMap<String,Object> map = new HashMap<String,Object>();
            // 遍历Claims中的所有键
            for (String key : claims.keySet()) {
                // 将每个键值对添加到HashMap中
                map.put(key, claims.get(key));
            }
            //重新签发长短token
            String newAccessToken = JwtUtil.createToken(map, 30);
            String newRefreshToken = JwtUtil.createToken(map, 3 * 24 * 60);
            //保存新的长短token到redis
            stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_EXPIRE(newAccessToken), newAccessToken, 30,TimeUnit.MINUTES);
            stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_REFRESH(newAccessToken), newRefreshToken, 3, TimeUnit.DAYS);
            //通过响应头将jwt响应给客户端
            response.setHeader("token", newAccessToken);
            //在跨域请求访问时 必须公开该请求头 前端才能获取到
            response.setHeader("Access-Control-Expose-Headers","token");
            //为旧的accessToken设置一个较短的过期时间，防止并发情况下重复续期
            stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_EXPIRE(accessToken), accessToken,1, TimeUnit.MINUTES);

            //通过延迟队列延迟删除旧的长token
            rabbitTemplate.convertAndSend("", "delay_delete_cache_queue", RedisKey.ACCESS_REFRESH(accessToken));
        }

        return true;
    }
}
